import re
import shlex
import json
import math

INDENT = 4
PRINTLINE = 80


def print_key_val(init, value, pre_indent=0, end=','):
    """Print the key and value and insert it into the code list.
    :param init: string to initialize value e.g.
                 "'key': " or "url = "
    :param value: value to print in the dictionary
    :param pre_indent: optional param to set the level of indentation,
                       defaults to 0
    :param end: optional param to set the end, defaults to comma
    """
    indent = INDENT * pre_indent
    # indent is up to the first single quote
    start = indent + len(init)
    # 80 is the print line minus the starting indent
    # minus 2 single quotes, 1 space, and 1 backslash
    left = PRINTLINE - start - 4
    code = []
    code.append("{i}{s}'{v}'".format(i=" " * indent, s=init, v=value[:left]))
    if len(value) > left:
        code[-1] += " \\"
        # figure out lines by taking the length of the value and dividing by
        # chars left to the print line
        lines = int(math.ceil(len(value) / float(left)))
        for i in xrange(1, lines):
            delim = " \\"
            if i == lines - 1:
                delim = end
            code.append("{i}'{v}'{d}".format(i=" " * start,
                                             v=value[i * left:(i + 1) * left],
                                             d=delim))
    else:
        code[-1] += end
    return code


def dict_to_code(name, simple_dict):
    """Converts a dictionary to a python compatible key value pair
    >>> code = dict_to_code("cookies", cookies)
    :param name: name of the variable
    :param simple_dict: dictionary to iterate
    :return: python compatible code in a list
    """
    code = []
    if simple_dict:
        code.append("{} = {{".format(name))
        # check for python3
        try:
            for k, v in simple_dict.items():
                init = "'{k}': ".format(k=k)
                code += print_key_val(init, v, 1)
        except:
            for k, v in simple_dict.iteritems():
                init = "'{k}': ".format(k=k)
                code += print_key_val(init, v, 1)
        code.append("}\n")
    return code


class create_request():
    """Create request code from params
    >>> code = create_request("https://localhost:8080", None, "get", None,
    None)
    :param url: url requested
    :param method: method used e.g. get, post, delete, put
    :param cookies: dict of each cookie
    :param headers: dict of each header
    :param data: optional param to provided data to the request
    :return: python compatible code in a list
    """

    def __init__(self, url, method, cookies, headers, data=None, param=True):
        self.url, self.cookies, self.headers, self.data, self.method, self.param = url, cookies, headers, data, method, param
        if self.get_ct:
            self.method = 'post'
            try:
                self.data = json.loads(data)
            except Exception as e:
                pass

    @property
    def get_text(self):
        headers = self.repr_headers(self.headers)
        cookies = self.repr_headers(self.cookies)
        data = self.repr_headers(self.data)
        params = self.repr_headers(self.get_params)

        str = """# --coding:utf-8--
import requests

cookies = %s

headers = %s

url = '%s'

%s

print(response.text)

print(response)
""" % (cookies, headers, self.get_url, self.get_resp_str)
        if data:
            str = str.replace('import requests',
                              f'import requests\n\ndata = {data}')
        if params:
            str = str.replace('import requests',
                              f'import requests\n\nparams = {params}')

        return str

    @property
    def get_ct(self):
        ct = self.headers.get(
            'Content-Type') or self.headers.get('content-type') or ""
        if 'application/json' in ct or self.data:
            return True
        return False

    @property
    def get_is_json(self):
        ct = self.headers.get(
            'Content-Type') or self.headers.get('content-type') or ""
        if 'application/json' in ct:
            return True
        return False

    @property
    def get_method(self):
        if self.get_ct:
            return 'POST'

        return self.method.upper()

    @property
    def get_data(self):
        return self.data

    @property
    def get_url(self):
        return self.url.split('?')[0] if self.param else self.url

    @property
    def get_headers(self):
        return self.headers

    @property
    def get_resp_str(self):
        str = 'response = requests.'
        if self.get_method == 'GET':
            str += 'get(url=url, headers=headers)'
        else:
            str += 'post(url=url, headers=headers)'
        if self.data:
            if self.get_is_json:
                str = str.replace(')', ', json=data)')
            else:
                str = str.replace(')', ', data=data)')

        if self.cookies:
            str = str.replace(')', ', cookies=cookies)')
        if self.get_params:
            str = str.replace(')', ', params=params)')
        return str

    @property
    def get_params(self):
        params = {}
        if '?' not in self.url or self.param is False:
            return params
        else:
            urls = self.url.split('?')[1].split('&')
            for items in urls:
                item = items.split('=')
                k = item[0]
                v = item[1]
                params[k] = v
            return params

    def repr_headers(self, h):
        if h:
            repl_list = [
                ["{\"", "{\n    \""],
                ["\", \"", "\",\n    \""],
                ["null", "None"],
                ["true", "True"],
                ["false", "False"],
            ]
            h = json.dumps(h)
            for i in repl_list:
                k, v = i[0], i[1]
                h = h.replace(k, v)
            if h[-1] == '}':
                result = h[:-1] + '\n}'
            else:
                result = h

            return result
        else:
            return h

    def __str__(self):
        return self.get_text


def curl_to_python(command, ck=True, param=True):
    """Convert curl command to python script.
    :param command: curl command exported from Chrome's Dev Tools
    :return: python compatible code in a list
    """
    args = shlex.split(command)
    data = None
    method = 'GET'
    if '-X' in args:
        method = args[args.index('-X') + 1]
    if '--data-raw' in args:
        data = args[args.index('--data-raw') + 1]

    url = args[1]
    headers = {}
    cookies = {}
    for i, v in enumerate(args):
        if '-H' == v:
            myargs = args[i + 1].split(':')
            key = myargs[0].replace(
                '$Cookie', 'Cookie').replace('$cookie', 'cookie')
            print(key)
            value = ':'.join(myargs[1:]).strip()
            if key.lower() == 'cookie' and ck:
                values = value.split(';')
                for v in values:
                    try:
                        v = v.split('=')
                        cookies[v[0].strip()] = v[1]
                    except:
                        pass
            else:
                headers[key] = value
    code = create_request(url, method, cookies, headers, data, param)
    return code


def res_to_curl(res):
    """converts a requests response to a curl command
    >>> res = requests.get('http://www.example.com')
    >>> print(res_to_curl(res))
    curl 'http://www.example.com/' -X 'GET' ...
    Source: http://stackoverflow.com/a/17936634
    :param res: request object
    """
    req = res.request
    command = "curl '{uri}' -X '{method}' -H {headers}"
    headers = ["{}: {}".format(k, v) for k, v in req.headers.items()]
    header_line = " -H ".join(['"{}"'.format(h) for h in headers])
    if req.method == "GET":
        return command.format(method=req.method, headers=header_line,
                              uri=req.url)
    else:
        command += " --data-binary '{data}'"
        return command.format(method=req.method, headers=header_line,
                              data=req.body, uri=req.url)


def curl_main(command=None, ck=True, param=True):
    """Main entry point.
    Purposely didn't use argparse or another command line parser to keep this
    script simple.
    """
    if not command:
        command = 'curl "http://www.example.com" ' + \
                  '-H "Pragma: no-cache" ' + \
                  '-H "Accept-Encoding: gzip, deflate" ' + \
                  '-H "Accept-Language: en-US,en;q=0.8"'
    code = curl_to_python(command, ck=ck, param=param)
    return code


def curl_parse(String):
    try:
        output = curl_to_python(String).get_text
    except:
        output = ''
    return output
